package scales.xml.parser.pull
import scalaz.Equal, scalaz.Equal._, scalaz.Scalaz._
import scales.utils._
import scales.xml.{
PullType,
noXmlPath,
XmlPath,
QName,
ScalesXml,
Elem, EndElem,
XmlItem,
addAndFocus,
addChild,
XmlBuilder,
parser,
impl => ximpl
}
import scales.xml.parser.strategies.{MemoryOptimisationStrategy, OptimisationToken}
import collection.FlatMapIterator
trait PullIteratees {
import scalaz.{IterV, Enumerator, Input, EphemeralStream}
import scalaz.IterV._
type QNamesMatch = (List[QName], Option[XmlPath])
def onQNames(qnames: List[QName]): ResumableIter[PullType, QNamesMatch] = onQNamesI(qnames)(ScalesXml.qnameEqual)
def onQNamesI(qnames: List[QName])(implicit qe: Equal[QName]): ResumableIter[PullType, QNamesMatch] = {
lazy val starter = Cont(step(Nil, (qnames.head, 0), qnames.tail.map((_, 0)), noXmlPath, false))
def step(before: List[(QName, Int)], focus: (QName, Int), toGo: List[(QName, Int)], path: XmlPath, collecting: Boolean)(s: Input[PullType]): ResumableIter[PullType, QNamesMatch] =
s(el = { e =>
e match {
case Left(elem@Elem(q, a, n)) => {
val nfocus = if (q === focus._1) (focus._1, focus._2 + 1)
else focus
val npath = addAndFocus(path, elem)
val shouldCollect = collecting || (toGo.isEmpty && q === focus._1)
Cont(
if ((!toGo.isEmpty) && q === focus._1)
step(before :+ focus, toGo.head, toGo.tail, npath, false)
else
step(before, nfocus, toGo, npath, shouldCollect))
}
case Left(x: XmlItem) =>
if (collecting)
Cont(step(before, focus, toGo, addChild(path, x), true))
else
Cont(step(before, focus, toGo, path, false))
case Right(EndElem(q, n)) =>
if (q === focus._1) {
val ncfocus = (focus._1, focus._2 - 1)
if (toGo.isEmpty && ncfocus._2 == 0)
Done(((qnames, Some(path)),
Cont(step(before, ncfocus, toGo,
path.removeAndUp.getOrElse(noXmlPath), false))), IterV.Empty[PullType])
else {
if (before.isEmpty)
starter
else {
if (collecting)
Cont(step(before, ncfocus, toGo, path.zipUp, true))
else {
val nfocus = before.last
val nbefore = before.dropRight(1)
Cont(step(nbefore, nfocus, focus :: toGo,
path.removeAndUp.getOrElse(noXmlPath), false
))
}
}
}
} else {
Cont(step(before, focus, toGo,
if (collecting)
path.zipUp
else
path.removeAndUp.getOrElse(noXmlPath), collecting))
}
}
},
empty = Cont(step(before, focus, toGo, path, false)),
eof = Done(((qnames, None), starter), IterV.EOF[PullType]))
if (qnames.isEmpty) error("Qnames is empty")
starter
}
type PeekMatch = Option[XmlPath]
def skipv(downTo: Int*): IterV[PullType, PeekMatch] = skip(List(downTo: _*))
def skip(downTo: => List[Int]): IterV[PullType, PeekMatch] = {
lazy val dEof: IterV[PullType, PeekMatch] = Done(None, IterV.EOF[PullType])
def step(before: List[Int], pos: List[Int], toGo: List[Int], path: XmlPath)(s: Input[PullType]): IterV[PullType, PeekMatch] =
s(el = { e =>
e match {
case Left(elem@Elem(q, a, n)) => {
lazy val npath = addAndFocus(path, elem)
val npos = pos.head + 1 :: pos.tail
val could = toGo.head == npos.head
if (pos.size == (before.size + 1))
if (toGo.size == 1 && could)
Done(Some(npath), IterV.Empty[PullType])
else if (npos.head > toGo.head)
dEof
else if (could)
Cont(step(before :+ toGo.head, 0 :: npos, toGo.tail, npath))
else
Cont(step(before, 0 :: npos, toGo, npath))
else
Cont(step(before, 0 :: npos, toGo, npath))
}
case Left(x: XmlItem) =>
Cont(step(before, pos, toGo, path))
case Right(EndElem(q, n)) =>
if (pos.size > 0 && pos.size == before.size + 1)
Cont(step(before.dropRight(1), pos.tail, before.last :: toGo, path.removeAndUp().getOrElse(noXmlPath)))
else
Cont(step(before, pos.tail, toGo, path.removeAndUp().getOrElse(noXmlPath)))
}
},
empty = Cont(step(before, pos, toGo, path)),
eof = dEof
)
Cont(step(List[Int](), List(0), 1 :: downTo, noXmlPath))
}
def iterate(path: List[QName], xml: XmlPull): FlatMapIterator[XmlPath] = iterate(path, xml.it)
def iterate(path: List[QName], xml: Iterator[PullType]): FlatMapIterator[XmlPath] =
new Iterate(path, xml)
def iterateI(path: List[QName], xml: XmlPull)(implicit qe: Equal[QName]): FlatMapIterator[XmlPath] = iterateI(path, xml.it)(qe)
def iterateI(path: List[QName], xml: Iterator[PullType])(implicit qe: Equal[QName]): FlatMapIterator[XmlPath] =
new Iterate(path, xml, qe)
}
class Iterate(path: List[QName], xml: Iterator[PullType], qe: Equal[QName] = ScalesXml.qnameEqual) extends FlatMapIterator[XmlPath] {
import ScalesXml.{qnameEqual => _, _}
import ScalesUtils._
import ximpl.TreeProxies
val qnames = path
implicit val iqe = qe
if (qnames.isEmpty) error("QNames is empty")
var before: List[(QName, Int)] = _
var focus: (QName, Int) = _
var toGo: List[(QName, Int)] = _
var proxies: TreeProxies = new TreeProxies()
var collecting: Boolean = _
def reset {
set(Nil, (qnames.head, 0), qnames.tail.map((_,0)),
proxies.reuse, false)
}
reset
def set(before: List[(QName, Int)], focus: (QName, Int), toGo: List[(QName, Int)], proxies: TreeProxies, collecting: Boolean) {
this.before = before
this.focus = focus
this.toGo = toGo
this.proxies = proxies
this.collecting = collecting
}
def getNext = step
var cur = getNext
def hasNext = cur ne null
def next = {
val t = cur
cur = getNext
t
}
def step : XmlPath = {
var res : XmlPath = null.asInstanceOf[XmlPath]
while(xml.hasNext && res == null) {
val e = xml.next
e match {
case Left(elem@Elem(q, a, n)) => {
val nfocus =
if (q === focus._1) (focus._1, focus._2 + 1)
else focus
proxies.beginSub(elem, XmlBuilder())
val shouldCollect = collecting || (toGo.isEmpty && q === focus._1)
if ((!toGo.isEmpty) && q === focus._1)
set(before :+ focus, toGo.head, toGo.tail, proxies, false)
else
set(before, nfocus, toGo, proxies, shouldCollect)
}
case Left(x: XmlItem) =>
if (collecting) {
proxies.addChild(x)
set(before, focus, toGo, proxies, true)
}
else
set(before, focus, toGo, proxies, false)
case Right(EndElem(q, n)) =>
if (q === focus._1) {
val ncfocus = (focus._1, focus._2 - 1)
if (toGo.isEmpty && ncfocus._2 == 0) {
res = proxies.proxyPath
set(before, ncfocus, toGo,
proxies, false)
}
else {
if (before.isEmpty)
reset
else {
if (collecting) {
proxies.elementEnd()
set(before, ncfocus, toGo, proxies, true)
}
else {
val nfocus = before.last
val nbefore = before.dropRight(1)
set(nbefore, nfocus, focus :: toGo,
proxies.proxyRemoveAndUp(), false
)
}
}
}
} else {
set(before, focus, toGo,
if (collecting) {
proxies.elementEnd
proxies
} else
proxies.proxyRemoveAndUp(), collecting)
}
}
}
res
}
}